import { angleToRad, MathTool, PickType } from '@amcax/base';
import { getFaceFrame } from '@app-base/common/utils';
import { getNearFaceNormalAndUp } from '@app-base/common/utils/camera';
import { eventCenterManager } from '@app-cad/common/events/EventCenterManager';
import { MessageTip } from '@app-cad/common/utils/MessageTip';
import { ctx2, docMgr } from '@app-cad/module';
import { Box3, Sphere, Vector3 } from 'three-legacy';
import { EasingTypes, rotateAnimation } from './animate';

export enum Types {
  FRONT = 'front',
  BACK = 'back',
  LEFT = 'left',
  RIGHT = 'right',
  TOP = 'top',
  BOTTOM = 'bottom',
  ISO = 'iso',
}

type Frame = {
  z: Vector3;
  x: Vector3;
};

const sceneSetup = ctx2.viewer.sceneSetup;
const maxDistance = 500000;

const getFrame = (type: Types): Frame => {
  switch (type) {
    case Types.FRONT:
      return {
        z: new Vector3(0, -1, 0),
        x: new Vector3(1, 0, 0),
      };
    case Types.BACK:
      return {
        z: new Vector3(0, 1, 0),
        x: new Vector3(-1, 0, 0),
      };
    case Types.LEFT:
      return {
        z: new Vector3(-1, 0, 0),
        x: new Vector3(0, -1, 0),
      };
    case Types.RIGHT:
      return {
        z: new Vector3(1, 0, 0),
        x: new Vector3(0, 1, 0),
      };
    case Types.TOP:
      return {
        z: new Vector3(0, 0, 1),
        x: new Vector3(1, 0, 0),
      };
    case Types.BOTTOM:
      return {
        z: new Vector3(0, 0, -1),
        x: new Vector3(1, 0, 0),
      };
    case Types.ISO:
      return {
        z: new Vector3(1, -1, 1),
        x: new Vector3(1, 1, 0),
      };
    default:
      break;
  }
  return null;
};

const getRotateInfo = (
  center: Vector3,
  diameter: number,
  faceNormal: Vector3,
  faceRightDir: Vector3,
  zoomFactor = 0.5,
) => {
  const result: {
    cameraUp?: Vector3;
    cameraPos?: Vector3;
    target?: Vector3;
    far?: number;
    zoom?: number;
  } = {
    zoom: sceneSetup.camera.zoom,
  };

  let dist: number;

  if (sceneSetup.camera === sceneSetup.pCamera) {
    const { aspect } = sceneSetup.camera;
    let angle = angleToRad(sceneSetup.camera.fov / 2);
    if (aspect < 1) {
      angle = Math.atan(Math.tan(angle) * aspect);
    }
    result.far = diameter * 10;
    dist = (0.4 * diameter) / Math.tan(angle);
  } else {
    const clientSize = Math.max(
      sceneSetup.container.clientWidth,
      sceneSetup.container.clientHeight,
    );
    sceneSetup.camera.zoom = (clientSize * zoomFactor) / diameter;
    result.zoom = sceneSetup.camera.zoom;
    if (diameter < maxDistance) {
      diameter = maxDistance / 10;
    }
    result.far = diameter * 10;
    dist = diameter * 4;
  }

  result.target = center.clone();
  result.cameraPos = center.add(faceNormal.multiplyScalar(dist));
  result.cameraUp = faceNormal.clone().cross(faceRightDir).normalize();

  return result;
};

const fitView = async (frame: Frame, diameter: number, center: Vector3) => {
  if (!sceneSetup.rotateAnimationComplete) {
    return;
  }
  const originInfo = {
    cameraUp: sceneSetup.camera.up.clone(),
    cameraPos: sceneSetup.camera.position.clone(),
    target: sceneSetup.trackballControls.target.clone(),
    far: sceneSetup.camera.far,
    zoom: sceneSetup.camera.zoom,
  };
  sceneSetup.trackballControls.setFitZoom(sceneSetup.camera.zoom);
  const lookAt = sceneSetup.camera.getWorldDirection(new Vector3());
  const dot =
    (lookAt.clone().dot(frame.z.clone()) / lookAt.length()) *
      frame.z.length() ===
    1;
  if (dot) {
    //为正对面创建过渡面
    const interimInfo =
      frame &&
      getRotateInfo(center, diameter, frame.x, frame.z.clone().negate());
    await rotateAnimation(
      sceneSetup,
      originInfo,
      interimInfo,
      250,
      EasingTypes.IN,
    );

    const newParams = sceneSetup.getVisibleBoundingInfo();
    const finalInfo =
      frame &&
      getRotateInfo(newParams.center, newParams.diameter, frame.z, frame.x);
    const interimCameraInfo = {
      cameraUp: sceneSetup.camera.up.clone(),
      cameraPos: sceneSetup.camera.position.clone(),
      target: sceneSetup.trackballControls.target.clone(),
      far: sceneSetup.camera.far,
      zoom: sceneSetup.camera.zoom,
    };
    await rotateAnimation(
      sceneSetup,
      interimCameraInfo,
      finalInfo,
      250,
      EasingTypes.OUT,
    );
  } else {
    const rotateInfo =
      frame && getRotateInfo(center, diameter, frame.z, frame.x);
    await rotateAnimation(sceneSetup, originInfo, rotateInfo, 500);
  }

  sceneSetup.updateControlsAndHelpers();
  MathTool.debounce(sceneSetup.updatePickSenceTexture, 150);
};

const rotateView = (type: Types) => {
  const frame = getFrame(type);
  const { diameter, center } = sceneSetup.getVisibleBoundingInfo();
  fitView(frame, diameter, center);
};

const verticalObject = async () => {
  const event = eventCenterManager.activeEventCenter as any;
  const pickedObject = event.picker?.selectedMesh;
  if (pickedObject.assisObj && pickedObject?.idInfo?.t === PickType.PLANE) {
    const planeframe = await getFaceFrame(pickedObject);
    const { normal, up } = getNearFaceNormalAndUp(planeframe);

    const frame: Frame = {
      z: normal.clone().normalize(),
      x: up.clone().cross(normal).normalize(),
    };
    if (
      pickedObject.meshId === 'XOY' ||
      pickedObject.meshId === 'XOZ' ||
      pickedObject.meshId === 'YOZ'
    ) {
      const { diameter, center } = sceneSetup.getVisibleBoundingInfo();
      if (diameter === 0 || !center) {
        return;
      }
      fitView(frame, diameter, center);
    } else {
      const { diameter, center } = getFrameBoundingInfo(pickedObject.assisObj);
      if (diameter === 0 || !center) {
        return;
      }
      fitView(frame, diameter, center);
    }
  } else if (pickedObject.assisObj) {
    MessageTip.warning('非平面不可用，请选择平面');
  } else {
    MessageTip.warning('当前未选择平面');
  }
};

const getFrameBoundingInfo = (frameObj) => {
  const box = new Box3();
  box.expandByObject(frameObj);
  const target = new Sphere();
  box.getBoundingSphere(target);
  const center = frameObj.csys?.getOriginVc3();
  // const diameter = box.getCenter(new Vector3()).distanceTo(box.max);
  const diameter = target.radius;
  return { center, diameter };
};

const sketchHomeView = async () => {
  const { frame } = docMgr.getDocumentObjectById(docMgr.getCurrentSketchDxid());

  const { up, normal } = getNearFaceNormalAndUp([
    frame[0],
    new Vector3(...frame[1]).normalize().toArray(),
    new Vector3(...frame[2]).normalize().toArray(),
  ]);

  ctx2.viewer.sceneSetup.rotateView(normal, up);
};

// 基本视图
export function frontView() {
  rotateView(Types.FRONT);
  return null;
}

export function backView() {
  rotateView(Types.BACK);
  return null;
}

export function leftView() {
  rotateView(Types.LEFT);
  return null;
}

export function rightView() {
  rotateView(Types.RIGHT);
  return null;
}

export function topView() {
  rotateView(Types.TOP);
  return null;
}

export function bottomView() {
  rotateView(Types.BOTTOM);
  return null;
}

// 等轴测视图
export function isometricView() {
  rotateView(Types.ISO);
  return null;
}

// 正视于
export async function verticalView() {
  if (eventCenterManager.isSketchMode) {
    // 草图环境
    sketchHomeView();
  } else {
    // 3d环境
    verticalObject();
  }
  return null;
}
